package com.wonders.frame.service;

import com.gcr.util.StringUtil;
import com.wonders.frame.common.Enum.*;
import com.wonders.frame.common.QueryForm;
import com.wonders.frame.common.Utils;
import com.wonders.frame.common.util.DateUtil;
import com.wonders.frame.common.util.Md5Util;
import com.wonders.frame.common.util.PageUtil;
import com.wonders.frame.entity.*;
import com.wonders.frame.entity.Tree.SelectTreeNodeData;
import com.wonders.frame.entity.form.AuthAccountForm;
import com.wonders.frame.entity.form.AuthUserForm;
import com.wonders.frame.entity.form.PasswordForm;
import com.wonders.frame.repository.*;
import com.wonders.frame.repository.core.CoreRepository;
import com.wonders.frame.service.Core.CoreCurdService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class AuthAccountService extends CoreCurdService<AuthAccount> {
    Logger log = LoggerFactory.getLogger(AuthAccountService.class);

    @Autowired
    private AuthAccountRepository repo;
    @Autowired
    private AuthUserRepository uRepo;
    @Autowired
    private AuthOrgaService orgaService;
    @Autowired
    private AuthAccountRoleRepository arRepo;
    @Autowired
    private AuthRoleRepository rRepo;

    @Autowired
    private AuthOrgaRepository authOrgaRepository;

    @Autowired
    private AuthPermissionService perService;
    @Autowired
    private AuthUserService authUserService;

    @Autowired
    private AuthAccountRoleRepository authAccountRoleRepository;


    protected CoreRepository getRepository() {
        return repo;
    }

    //判断唯一性 账号
    public boolean checkUniqueAccount(AuthAccountForm authAccountForm){
        //修改
        if(!StringUtils.isEmpty(authAccountForm.getId())){
            AuthUser authUser=new AuthUser();
            authUser.setId(authAccountForm.getUserId());
            //判断是否修改了账号
            AuthAccount authAccount =repo.findByUserAndRemoved(authUser, RemovedCodeEnum.AVAILABLE.getCode());
            if(authAccountForm.getAccount().equals(authAccount.getAccount())){
                return true;
            }
        }
        //根据账号查询修改后的账号是否存在
        List<AuthAccount> list =repo.findByAccountAndRemoved(authAccountForm.getAccount(),RemovedCodeEnum.AVAILABLE.getCode());
        if(CollectionUtils.isEmpty(list)){
            return true;
        }
        return false;
    }

    @Transactional
    public AuthAccount save(AuthAccountForm accountForm, String userName)  {

        AuthUser user = new AuthUser();
        //新增
        if (StringUtil.isEmpty(accountForm.getUserId())) {
            user.setCrtDate(new Date());
            user.setCrtUser(userName);

        } else {
            user=uRepo.getOne(accountForm.getUserId());
        }

        //获取机构 和机构的顶层组织id
        String orgaId=authUserService.getOrgaId(accountForm.getDeptId());
        //获取部门Id
        String deptId=accountForm.getDeptId();
        //通过部门ID查找上层机构
        AuthOrga orga=authOrgaRepository.getOne(deptId);
        if(deptId.equals(orgaId)){
            user.setOrgaName(orga.getOrgaName());
        }else{
            AuthOrga orga1=authOrgaRepository.getOne(orgaId);
            user.setOrgaName(orga1.getOrgaName()+"("+orga.getOrgaName()+")");
        }

        user.setIsadmin(accountForm.getIsadmin());
        user.setSex(accountForm.getSex());
        user.setPosition(accountForm.getPosition());

        user.setCertnum(accountForm.getCertnum());
        user.setPhone(accountForm.getPhone());

        user.setUserName(accountForm.getUserName());
        user.setAddress(accountForm.getAddress());
        user.setUpdDate(new Date());
        user.setUpdUser(userName);
        user.setRemoved(RemovedCodeEnum.AVAILABLE.getCode());


        uRepo.save(user);

        AuthAccount account = new AuthAccount();
        if (StringUtil.isEmpty(accountForm.getId())) {
            account.setCrtUser(userName);
            account.setCrtDate(new Date());
            //设置状态为正常 0正常  1注销
            account.setAccountStatus(0);
            //设置密码错误次数
            account.setErrorCount(0);

            account.setPassword(Md5Util.getMd5(accountForm.getAccount() + accountForm.getPassword()));

            log.debug("new account");
        } else {
            account.setPassword(accountForm.getPassword());
            account.setId(accountForm.getId());
            account.setUpdUser(userName);

            AuthAccount account1=repo.getOne(accountForm.getId());
            account.setAccountStatus(account1.getAccountStatus());
            account.setErrorCount(account1.getErrorCount());

            List<String> roleIds = arRepo.findRoleIdByAccountId(account.getId());
            List<AuthRole> roles = rRepo.findAllById(roleIds);
            account.setRoles(new HashSet<>(roles));
            log.debug("modify account");

        }
        try {
            if(!StringUtils.isEmpty(accountForm.getExpDate())){
                account.setExpDate(DateUtil.stringToDate(accountForm.getExpDate(),DateUtil.DATE));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        account.setUser(user);
        account.setOrgaId(orgaId);
        account.setDeptId(deptId);
        account.setAccount(accountForm.getAccount());
        account.setUpdDate(new Date());
        account.setType(accountForm.getType());
        account.setRemoved(RemovedCodeEnum.AVAILABLE.getCode());
        account=repo.save(account);
        return account;
    }

    public AuthAccount getAccbyAccount(String account) {
        Map<String, Object> map = new HashMap<String, Object>();
        List<String> fatch = new ArrayList<String>();
//        fatch.add(CoreCurdService.FATCH_ROLE_PER);
//        fatch.add("roles");
        map.put("account_eq", account);
        AuthAccount acc = findOneByParams(map, fatch);
        return acc;
    }

    public LoginCodeEnum checkLogin(AuthAccount acc, String account, String password) {
        //1.验证用户名密码

        if (acc == null) {
            return LoginCodeEnum.ACCOUNT_NOT_EXIST;
        }
        if (!Objects.equals(password, acc.getPassword())) {
            //密码错误  错误次数加1
            acc.setErrorCount(acc.getErrorCount()+1);
            repo.save(acc);
            return LoginCodeEnum.PASSWORD_IS_WRONG;
        }

        //登录成功 设置登录错误次数为0
        acc.setErrorCount(0);
        repo.save(acc);
        return LoginCodeEnum.SUCCESS;
    }

    //获取用户权限
    public List<AuthPermission> getAllPermissionByRoles(List<AuthRole> roles) {
        List<AuthPermission> list = new ArrayList<AuthPermission>();
        //map<权限Id,对应权限> -- 用户拥有的权限Id
        Map<String, AuthPermission> map = new HashMap<String, AuthPermission>();
        //treeMap<parentId,若干子权限> -- 用户权限树形结构
        Map<String, List<AuthPermission>> treeMap = new HashMap<String, List<AuthPermission>>();

        if (roles == null && roles.size() == 0) {
            return list;
        }
        boolean ifAdmin = false;
        // admin 获取所有permission
        for (AuthRole role : roles) {
            if(Objects.equals(role.getRoleCode(), "admin") ){
                ifAdmin = true;
            }
        }

        if(ifAdmin){
            for (AuthPermission per : perService.findAllByParams(new HashMap<>())) {
                map.put(per.getId(), per);
            }
        }else{
            //多个role中可能存在重复的permission，以下去重
            for (AuthRole role : roles) {
                for (AuthPermission per : role.getPres()) {
                    //qiu 修改 如果父节点已存在 说明已有这个权限 不需要加进去
                    if (map.containsKey(per.getId())||map.containsKey(per.getParentId())) {
                        continue;
                    }
                    //qiu 如果先加了子节点 后面才进来父节点 也需要把子节点移除
                    if(!CollectionUtils.isEmpty(per.getPerChildren())){
                        for(AuthPermission sonPer:per.getPerChildren()){
                            if(map.containsKey(sonPer.getId())){
                                map.remove(sonPer.getId());
                            }
                        }
                    }
                    map.put(per.getId(), per);
                }
            }
        }

        //初始化菜单逻辑：
        /*
        role-permission中间表中保存的逻辑为：
        如果根节点
         */
        String parentId = "";
        for (Map.Entry<String, AuthPermission> entry : map.entrySet()) {
            parentId = entry.getValue().getParentId();

            if (!StringUtil.isEmpty(parentId)) {
                if (treeMap.get(parentId) == null) {
                    List<AuthPermission> pers = new ArrayList<AuthPermission>();
                    pers.add(entry.getValue());
                    treeMap.put(parentId, pers);
                } else {
                    treeMap.get(parentId).add(entry.getValue());
                }
            }

        }
        String key = "";
        List<AuthPermission> value;
        for (Map.Entry<String, List<AuthPermission>> entry : treeMap.entrySet()) {
            key = entry.getKey();
            value = entry.getValue();
            if (Objects.equals(key, "all")) {
                if(ifAdmin){
                    continue;
                }
                list.addAll(value);
            } else {
                AuthPermission ap = perService.findById(key);
                ap.setPerChildren(value);
                list.add(ap);
            }
        }

        Collections.sort(list);
        List<AuthPermission> ll = new ArrayList<AuthPermission>();
        for (int i = 0; i < list.size(); i++) {
            if (Objects.equals(list.get(i).getCode(), "allApi")) {
                continue;
            }
            ll.add(list.get(i));
        }
        return ll;
    }


    public List<AuthPermission> getApiPermission(List<AuthRole> roles) {
        List<AuthPermission> pers = new ArrayList<AuthPermission>();

        for (AuthRole r : roles) {
            for (AuthPermission per : r.getPres()) {
                if (Objects.equals(AuthTypeEnum.API.getCode(), per.getAuthType())) {
                    pers.add(per);
                }
            }
        }
        return pers;
    }

    public AuthAccount findByUserId(String userId) {
        AuthUser authUser = new AuthUser();
        authUser.setId(userId);
        return repo.findByUserAndRemoved(authUser, RemovedCodeEnum.AVAILABLE.getCode());
    }

    //排序权限
    public List<AuthPermission> sortAuthPermission(List<AuthPermission> list){
        if(CollectionUtils.isEmpty(list)){
            return list;
        }

        for(AuthPermission authPermission:list){
            List<AuthPermission> child=authPermission.getPerChildren();
            if(CollectionUtils.isEmpty(child)){
                continue;
            }
            child=child.stream().sorted((a, b) -> a.getSourceIndex() - b.getSourceIndex()).collect(Collectors.toList());
            authPermission.setPerChildren(child);
            sortAuthPermission(child);
        }

        return list;
    }



    public List<SelectTreeNodeData> getUserByOrgaId(String orgaId) {
        List<AuthAccount> list = repo.findByOrgaIdAndRemoved(orgaId, RemovedCodeEnum.AVAILABLE.getCode());

        List<SelectTreeNodeData> treeList = new ArrayList<>();
        for (AuthAccount authAccount : list) {
            SelectTreeNodeData selectTreeNodeData = new SelectTreeNodeData();
            AuthUser authUser = authAccount.getUser();
            if (!StringUtils.isEmpty(authUser)) {
                //没删除的user
                if (authUser.getRemoved() == RemovedCodeEnum.AVAILABLE.getCode()) {
                    selectTreeNodeData.setTitle(authUser.getUserName());
                    selectTreeNodeData.setKey(authUser.getId());
                    selectTreeNodeData.setValue(authUser.getUserName());
                    treeList.add(selectTreeNodeData);
                }
            }
        }
        return treeList;
    }

    public Page<AuthAccount> getUserList(AuthUserForm authUserForm) {
        List<String> pIds = new ArrayList<String>();
        Map<String, Object> map = InitCommonSourceEnum.getCommonSource(CommonSourceKeyEnum.ORGA.name());
        //查询所有机构 超级管理员查所有
        if (StringUtils.isEmpty(authUserForm.getOrgaId())) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (RemovedCodeEnum.AVAILABLE.getCode() == ((AuthOrga) entry.getValue()).getRemoved()) {
                    pIds.add(((AuthOrga) entry.getValue()).getId());
                }
            }
        } else {
            //其他的查询该机构下面的所有节点
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (authUserForm.getOrgaId().equals(entry.getKey())) {
                    //获取机构的子节点
                    AuthOrga authOrga = (AuthOrga) entry.getValue();
                    pIds = getChildOrgaId(pIds, authOrga);
                }
            }
            //当前机构id
            pIds.add(authUserForm.getOrgaId());
        }

        authUserForm.setOrgaIdList(pIds);

        Specification specification = initSpecification(authUserForm);
        Pageable pageable = getPage(authUserForm);

        return repo.findAll(specification, pageable);
    }

    private Specification initSpecification(AuthUserForm form) {
        return new Specification<AuthAccount>() {
            @Override
            public Predicate toPredicate(Root<AuthAccount> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicateArrayList = new ArrayList<>();
                if (!StringUtils.isEmpty(form.getUserName())) {
                    Predicate predicate = cb.like(root.join("user").get("userName").as(String.class), form.getUserName() + "%");
                    predicateArrayList.add(predicate);
                }
                if (!StringUtils.isEmpty(form.getPhone())) {
                    Predicate predicate = cb.like(root.join("user").get("phone").as(String.class), form.getPhone() + "%");
                    predicateArrayList.add(predicate);
                }
                if (!StringUtils.isEmpty(form.getStatus())) {
                    Predicate predicate = cb.equal(root.join("user").get("status").as(String.class), form.getStatus());
                    predicateArrayList.add(predicate);
                }
                if (!CollectionUtils.isEmpty(form.getOrgaIdList())) {
                    Path path = root.get("orgaId");
                    CriteriaBuilder.In<Object> in = cb.in(path);
                    for (String string : form.getOrgaIdList()) {
                        in.value(string);
                    }
                    in.value(null);
                    Predicate predicate = cb.and(in);
                    predicateArrayList.add(predicate);
                }

                //设置两表状态为正常的
                Predicate p1 = cb.equal(root.join("user").get("removed").as(Integer.class), RemovedCodeEnum.AVAILABLE.getCode());
                predicateArrayList.add(p1);
                Predicate p2 = cb.equal(root.get("removed").as(Integer.class), RemovedCodeEnum.AVAILABLE.getCode());
                predicateArrayList.add(p2);

                Predicate[] p = new Predicate[predicateArrayList.size()];
                query.where(cb.and(predicateArrayList.toArray(p)));
                return query.getRestriction();
            }
        };

    }

    private Pageable getPage(AuthUserForm form) {
        Sort sort;
        String sortStr = form.getSort();
        if (StringUtil.isEmpty(sortStr)) {
            sort = new Sort(Sort.Direction.DESC, "updDate");
            return PageRequest.of(form.getPageNum() - 1, form.getPageSize(), sort);
        } else {
            if (sortStr.contains(SortSplitChar.getSplitChar())) {
                sort = new Sort(Sort.Direction.DESC, sortStr.split(SortSplitChar.getSplitChar()));
            } else {
                sort = new Sort(Sort.Direction.DESC, sortStr);
            }
            return PageRequest.of(form.getPageNum() - 1, form.getPageSize(), sort);
        }
    }

    //获取所有子机构id 包括自己
    private List<String> getChildOrgaId(List<String> list, AuthOrga authOrga) {

        //不为空就继续循环
        List<AuthOrga> childrenList = authOrga.getOrgaChildren();
        if (!CollectionUtils.isEmpty(childrenList)) {
            for (AuthOrga orga : childrenList) {
                if (orga.getRemoved() == RemovedCodeEnum.AVAILABLE.getCode()) {
                    list.add(orga.getId());
                    getChildOrgaId(list, orga);
                }
            }
        }
        return list;
    }

    public AuthAccount resetPassword(String id) {
        AuthAccount authAccount = repo.getOne(id);
        authAccount.setPassword(Md5Util.getMd5(authAccount.getAccount() + Utils.RESET_PASSWORD));
        return repo.save(authAccount);
    }

    //验证原密码是否正确
    public boolean isOldPassword(String id, String password) {
        AuthAccount authAccount = repo.getOne(id);
        //原密码加密 对比数据库加密后的密码
        String md5Password = Md5Util.getMd5(authAccount.getAccount() + password);
        if (md5Password.equals(authAccount.getPassword())) {
            return true;
        }
        return false;
    }

    public AuthAccount updatePassword(PasswordForm passwordForm) {
        AuthAccount authAccount = repo.getOne(passwordForm.getId());
        authAccount.setPassword(Md5Util.getMd5(authAccount.getAccount() + passwordForm.getNewPassword()));
        return repo.save(authAccount);
    }

    public List<SelectTreeNodeData> getMember(List<AuthAccount> list) {
        List<SelectTreeNodeData> treeData = new ArrayList<>();
        for (AuthAccount authAccount : list) {
            SelectTreeNodeData selectTreeNodeData = new SelectTreeNodeData();
            AuthUser authUser = authAccount.getUser();
            if (!StringUtils.isEmpty(authUser)) {
                //没删除的user
                if (authUser.getRemoved() == RemovedCodeEnum.AVAILABLE.getCode()) {
                    selectTreeNodeData.setTitle(authUser.getUserName());
                    selectTreeNodeData.setKey(authUser.getId());
                    selectTreeNodeData.setValue(authUser.getUserName());
                    treeData.add(selectTreeNodeData);
                }
            }
        }
        return treeData;
    }

    public AuthAccount accountLogout(String id){
        AuthAccount authAccount=repo.getOne(id);
        //账户状态修改为注销
        authAccount.setAccountStatus(1);
        return repo.save(authAccount);
    }

    public AuthAccount accountEnable(String id){
        AuthAccount authAccount=repo.getOne(id);
        //账户状态修改为正常
        authAccount.setAccountStatus(0);
        return repo.save(authAccount);
    }

    public AuthAccount accountUnlock(String id){
        AuthAccount authAccount=repo.getOne(id);
        //登陆错误次数置零，可重新登录
        authAccount.setErrorCount(0);
        return repo.save(authAccount);
    }

    public void deleteAccounts(String [] accountIds){
        List<String> list = Arrays.asList(accountIds);
        List<AuthAccount> all = repo.findAllById(list);
        for(AuthAccount authAccount:all){
            AuthUser authUser = authAccount.getUser();
            authUser.setRemoved(RemovedCodeEnum.REMOVED.getCode());
            uRepo.save(authUser);
        }
        all.forEach(e->e.setRemoved(RemovedCodeEnum.REMOVED.getCode()));
        repo.saveAll(all);
    }

    public void deleteAccount(String accountId){
        AuthAccount account = repo.getOne(accountId);
        AuthUser user = account.getUser();
        user.setRemoved(RemovedCodeEnum.REMOVED.getCode());
        account.setRemoved(RemovedCodeEnum.REMOVED.getCode());
        repo.save(account);
        uRepo.save(user);
    }

    public void changeAccountOrga(String [] accountIds,String changeOrgaId){
        List<String> list = Arrays.asList(accountIds);
        List<AuthAccount> all = repo.findAllById(list);
        String orgaId=authUserService.getOrgaId(changeOrgaId);
        AuthOrga orga=authOrgaRepository.getOne(changeOrgaId);
        String orgaName="";
        if(changeOrgaId.equals(orgaId)){
            orgaName=orga.getOrgaName();
        }else{
            AuthOrga orga1=authOrgaRepository.getOne(orgaId);
            orgaName=orga1.getOrgaName()+"("+orga.getOrgaName()+")";
        }
        String finalOrgaName = orgaName;
        all.forEach(e->{
            e.getUser().setOrgaName(finalOrgaName);
            e.setDeptId(changeOrgaId);
            e.setOrgaId(orgaId);
        });
        repo.saveAll(all);
    }


    public List<SelectTreeNodeData> getPermissionByAccountId(String accountId){
        List<SelectTreeNodeData> nodes = new ArrayList<SelectTreeNodeData>();
        //根据accountId查询所有的role
        AuthAccount account = repo.getOne(accountId);
        Set<AuthRole> authRoles=  account.getRoles();
        List<AuthRole> roleList=new ArrayList<>(authRoles);
        List<AuthPermission> allPermissionByRoles = getAllPermissionByRoles(roleList);

        for(AuthPermission per : allPermissionByRoles){
            // 若存在parentNode，则该节点在child中显示，不需要加到根节点中
            nodes.add(getPermissionNode(per));
        }

        return nodes;
    }

    /**
     * 递归调用，初始化权限树菜单
     * @param per
     * @return
     */
    private SelectTreeNodeData getPermissionNode(AuthPermission per){
        SelectTreeNodeData nodeData = new SelectTreeNodeData();
        nodeData.setKey(per.getId());
        nodeData.setTitle(per.getComponentText());
        nodeData.setValue(per.getId());

        nodeData.setIcon("<Icon type='"+per.getComponentIconType()+"'/>");
        if(per.getPerChildren() != null){
            List<SelectTreeNodeData> nodes = new ArrayList<SelectTreeNodeData>();
            for(AuthPermission p : per.getPerChildren()){
                if(p.getRemoved()!=1){
                    nodes.add(getPermissionNode(p));
                }
            }
            nodeData.setChildren(nodes);
        }
        return nodeData;
    }

    //密码过期时间延后一个月（相较当前时间）
    public void extendExpDate(AuthAccount account){
        repo.save(account);
    }
}
